package org.yxing.palpalgps.services;

import java.io.IOException;
import java.io.StringReader;
import java.util.Date;
import java.util.List;

import org.apache.http.message.BasicNameValuePair;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.json.JSONException;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import org.yxing.palpalgps.PalPalGPSApplication;
import org.yxing.palpalgps.R;
import org.yxing.palpalgps.domain.UserInfo;
import org.yxing.palpalgps.http.AsyncRequest;
import org.yxing.palpalgps.http.AsyncRequest.FetchListener;
import org.yxing.palpalgps.models.ChatMessageModel;
import org.yxing.palpalgps.oauth.OAuth;
import org.yxing.palpalgps.provider.RecentChatsProvider;
import org.yxing.palpalgps.provider.RecentChatsProvider.RecentChats;
import org.yxing.palpalgps.sqlite.ChatDatabaseAdapter;
import org.yxing.palpalgps.utils.Helper;

import android.app.Service;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;

/**
 * service for chat
 * @author yuxing
 *
 */
public class XMPPService extends Service {
    
    private static final String TAG = XMPPService.class.getSimpleName();
    
    private static final int PORT = 5222;
    
    private static final int HANDLER_WHAT_PACKET = 1;
    private static final int HANDLER_WHAT_CONNECTED = 2;
    private static final int HANDLER_WHAT_UPDATE_PROFILE = 3;
    
    private String uid;
    
    private MyBinder binder = new MyBinder();
    
    private XMPPConnection conn;
    
    private ChatDatabaseAdapter mChatDatabaseADapter;
    
    public IBinder onBind(Intent intent) {
        return binder;
    }
    
    public void onCreate() {
        super.onCreate();
        Log.v(TAG, "onCreate");
        
        new Thread(){
            public void run() {
                connect();
                if (conn != null && conn.isConnected()) {
                    mHandler.sendEmptyMessage(HANDLER_WHAT_CONNECTED);
                }
            }
        }.start();
    }
    
    public int onStartCommand (Intent intent, int flags, int startId) {
        Log.v(TAG, "onStartCommand");
        //start connect
        mChatDatabaseADapter = new ChatDatabaseAdapter(this);
        mChatDatabaseADapter.open();
        return super.onStartCommand (intent, flags, startId);
    }
    
    
    public void onDestroy () {
        super.onDestroy();
        Log.v(TAG, "onDestroy");
        //close ChatDatabaseAdapter
        mChatDatabaseADapter.close();
        //disconnect
        if (conn != null && conn.isConnected())
            conn.disconnect();
    }
    
    public void onLowMemory () {
        super.onLowMemory();
    }
    
    /**
     * 连接
     */
    private void connect() {
        //get username and password
        PalPalGPSApplication application = (PalPalGPSApplication) getApplication();
        UserInfo user = application.getUser();
        uid = user.getId();
        
        ConnectionConfiguration config = new ConnectionConfiguration(Helper.getHost(), PORT);
        conn = new XMPPConnection(config);
        try {
            conn.connect();
            conn.login(user.getId(), user.getChatPassword(), Helper.RESOURCE);
        } catch (XMPPException e) {
            e.printStackTrace();
            stopSelf();
        }
    }
    
    /**
     * handler
     */
    private Handler mHandler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            super.handleMessage(msg);
            if (msg.what == HANDLER_WHAT_PACKET) {
                
                //receive packet
                Packet packet = (Packet) msg.obj;
                Log.v(TAG, packet.toXML());
                ChatMessageModel cmm = getMessage(packet);
                //insert message to database
                insertChatMessageToDatabase(cmm);
                //update recent chats
                recentChatsUpdate(uid, cmm.getFrom(), cmm.getEncodeBody());
                //display a toast message
                displayToast(getString(R.string.you_have_new_message));
            } else if (msg.what == HANDLER_WHAT_CONNECTED) {
                
                //add packed listener
                conn.addPacketListener(new PacketListener() {
                    public void processPacket(Packet packet) {
                        mHandler.sendMessage(mHandler.obtainMessage(HANDLER_WHAT_PACKET, packet));
                    }
                }, new PacketTypeFilter(Message.class));
            }
            
        }
    };
    
    /**
     * 更新最近聊天用户列表
     * @param uid
     * @param fuid
     * @param msg
     */
    private void recentChatsUpdate(String uid, String fuid, String msg) {
        String[] projection = {RecentChatsProvider.RecentChats._ID, RecentChats.QUANTITY, RecentChats.NAME};
        String selection = RecentChats.UID + "=? AND " + RecentChats.FUID + "=?";
        String[] selectionArgs = {uid, fuid};
        ContentValues values = new ContentValues();
        values.put(RecentChats.LAST_MSG, msg);
        
        Cursor c = getContentResolver().query(RecentChatsProvider.CONTENT_URI, projection, selection, selectionArgs, null);
        if (c.getCount() > 0) {
            c.moveToNext();
            long id = c.getLong(0);
            values.put(RecentChats.QUANTITY, c.getInt(1) + 1);
            String where = RecentChats._ID + "=?";
            String[] whereArgs = {Long.toString(id)};
            getContentResolver().update(RecentChatsProvider.CONTENT_URI, values, where, whereArgs);
            Log.v(TAG, "update");
            
            if (TextUtils.isEmpty(c.getString(2)))
                updateProfile(fuid);
            
        } else {
            values.put(RecentChats.QUANTITY, 1);
            values.put(RecentChats.UID, uid);
            values.put(RecentChats.FUID, fuid);
            getContentResolver().insert(RecentChatsProvider.CONTENT_URI, values);
            updateProfile(fuid);
            Log.v(TAG, "insert");
        }
    }
    
    private void updateProfile(final String fuid) {
        final AsyncRequest ar = new AsyncRequest(this);
        ar.setIsShowProgressDialog(false);
        new Thread(){
            public void run() {
                List<BasicNameValuePair> params = OAuth.createAuthParams();
                params.add(new BasicNameValuePair("fuid", fuid));
                ar.fetch("profile/info", params, new FetchListener() {
                    
                    @Override
                    public void onError(int code, String msg) {
                        
                    }
                    
                    @Override
                    public void onComplete(JSONObject response) {
                        try {
                            JSONObject result = response.getJSONObject("result");
                            String name = result.getString("name");
                            String portrait = result.getString("picture");
                            ContentValues values = new ContentValues();
                            values.put(RecentChats.NAME, name);
                            values.put(RecentChats.PORTRAIT, portrait);
                            String where = RecentChats.FUID + "=?";
                            String[] selectionArgs = new String[]{fuid};
                            getContentResolver().update(RecentChatsProvider.CONTENT_URI, values, where, selectionArgs);
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
                        
                    }
                });
            }
        }.start();
    }
    
    /**
     * insert chat message to database
     * @param cmm ChatMessageModel
     */
    public void insertChatMessageToDatabase(ChatMessageModel cmm) {
        mChatDatabaseADapter.insertData(uid, cmm);
    }
    
    /**
     * get message from packet
     * @param packet
     * @return
     */
    public ChatMessageModel getMessage(Packet packet) {
        ChatMessageModel cmm = new ChatMessageModel();
        cmm.setFrom(subUid(packet.getFrom()));
        cmm.setTo(subUid(packet.getTo()));
        cmm.setDate(new Date());
        try {
            XmlPullParser xpp = XmlPullParserFactory.newInstance().newPullParser();
            xpp.setInput(new StringReader(packet.toXML()));
            int eventType = xpp.getEventType();
            boolean body = false;
            while (eventType != XmlPullParser.END_DOCUMENT) {
                if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("body"))
                    body = true;
                else if (eventType == XmlPullParser.TEXT) {
                    if (body) {
                        cmm.setEncodeBody(xpp.getText());
                        break;
                    }
                }
                
                eventType = xpp.next();
            }
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return cmm;
    }
    
    public XMPPConnection getConn() {
        return conn;
    }
    
    /**
     * get ChatDatabaseAdapter
     * @return ChatDatabaseAdapter
     */
    public ChatDatabaseAdapter getChatDatabaseAdapter() {
        return mChatDatabaseADapter;
    }
    
    /**
     * 截取用户的前面部分的用户名
     * @param str
     * @return
     */
    public static final String subUid(String str) {
        if (str.indexOf("@") > 0)
            return str.substring(0, str.indexOf("@"));
        else
            return str;
    }
    
    private void displayToast(String text)  {
        Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
    }
    
    public class MyBinder extends Binder {
        public XMPPService getService() {
            return XMPPService.this;
        }
    }
}
